// RegularExpressionParser.java
import java.io.*;
import java.util.*;

import static java.lang.Character.isLetter;

public class RegularExpressionParser {

    private static int nodeNum = 0;
    // 创建新节点
    public static Node newNode() {
        Node newNode = new Node();
        newNode.nodeName = String.valueOf((char) (nodeNum + 65)); // 用大写字母表示节点名
        nodeNum++;
        return newNode;
    }

    // 添加连接符号 '+'（即两个单位之间的隐式连接）
    public static String addJoinSymbol(String addString) {
        int length = addString.length();
        StringBuilder returnString = new StringBuilder(2 * length + 2);  // 至多是两倍

        char first, second;
        for (int i = 0; i < length - 1; i++) {
            first = addString.charAt(i);
            second = addString.charAt(i + 1);
            returnString.append(first);

            // 添加 '+' 符号的情况，比如 ab, *b, a(, )b
            if (first != '(' && first != '|' && isLetter(second)) {
                returnString.append('+');
            } else if (second == '(' && first != '|' && first != '(') {
                returnString.append('+');
            }
        }

        // 添加最后一个字符
        returnString.append(addString.charAt(length - 1));
        String result = returnString.toString();
        System.out.println("加 '+' 后的表达式: " + result);
        return result;
    }

    // 处理 'a' 单元
    public static Elem actElem(char c) {
        Node startNode = newNode();
        Node endNode = newNode();

        Edge newEdge = new Edge();
        newEdge.startName = startNode;
        newEdge.endName = endNode;
        newEdge.tranSymbol = c;

        Elem newElem = new Elem();
        newElem.edgeCount = 0;
        newElem.edgeSet[newElem.edgeCount++] = newEdge;
        newElem.startName = newElem.edgeSet[0].startName;
        newElem.endName = newElem.edgeSet[0].endName;

        return newElem;
    }

    // 处理 'a|b' 单元
    public static Elem actUnit(Elem fir, Elem sec) {
        Elem newElem = new Elem();
        newElem.edgeCount = 0;
        Edge edge1 = new Edge(), edge2 = new Edge(), edge3 = new Edge(), edge4 = new Edge();

        Node startNode = newNode();
        Node endNode = newNode();

        edge1.startName = startNode;
        edge1.endName = fir.startName;
        edge1.tranSymbol = '#';

        edge2.startName = startNode;
        edge2.endName = sec.startName;
        edge2.tranSymbol = '#';

        edge3.startName = fir.endName;
        edge3.endName = endNode;
        edge3.tranSymbol = '#';

        edge4.startName = sec.endName;
        edge4.endName = endNode;
        edge4.tranSymbol = '#';

        elemCopy(newElem, fir);
        elemCopy(newElem, sec);

        newElem.edgeSet[newElem.edgeCount++] = edge1;
        newElem.edgeSet[newElem.edgeCount++] = edge2;
        newElem.edgeSet[newElem.edgeCount++] = edge3;
        newElem.edgeSet[newElem.edgeCount++] = edge4;

        newElem.startName = startNode;
        newElem.endName = endNode;

        return newElem;
    }

    // 拷贝元素
    public static void elemCopy(Elem dest, Elem source) {
        for (int i = 0; i < source.edgeCount; i++) {
            dest.edgeSet[dest.edgeCount + i] = source.edgeSet[i];
        }
        dest.edgeCount += source.edgeCount;
    }

    // 处理 'ab' 连接
    public static Elem actJoin(Elem fir, Elem sec) {
        for (int i = 0; i < sec.edgeCount; i++) {
            if (sec.edgeSet[i].startName.nodeName.equals(sec.startName.nodeName)) {
                sec.edgeSet[i].startName = fir.endName;
            } else if (sec.edgeSet[i].endName.nodeName.equals(sec.startName.nodeName)) {
                sec.edgeSet[i].endName = fir.endName;
            }
        }
        sec.startName = fir.endName;

        elemCopy(fir, sec);

        fir.endName = sec.endName;
        return fir;
    }

    // 处理 'a*' 闭包
    public static Elem actStar(Elem elem) {
        Elem newElem = new Elem();
        newElem.edgeCount = 0;
        Edge edge1 = new Edge(), edge2 = new Edge(), edge3 = new Edge(), edge4 = new Edge();

        Node startNode = newNode();
        Node endNode = newNode();

        edge1.startName = startNode;
        edge1.endName = endNode;
        edge1.tranSymbol = '#';

        edge2.startName = elem.endName;
        edge2.endName = elem.startName;
        edge2.tranSymbol = '#';

        edge3.startName = startNode;
        edge3.endName = elem.startName;
        edge3.tranSymbol = '#';

        edge4.startName = elem.endName;
        edge4.endName = endNode;
        edge4.tranSymbol = '#';

        elemCopy(newElem, elem);

        newElem.edgeSet[newElem.edgeCount++] = edge1;
        newElem.edgeSet[newElem.edgeCount++] = edge2;
        newElem.edgeSet[newElem.edgeCount++] = edge3;
        newElem.edgeSet[newElem.edgeCount++] = edge4;

        newElem.startName = startNode;
        newElem.endName = endNode;

        return newElem;
    }

    // 将后缀表达式转换为 NFA
    public static Elem expressToNFA(String expression) {
        int length = expression.length();
        char element;
        Elem elem, fir, sec;
        Stack<Elem> stack = new Stack<>();
        for (int i = 0; i < length; i++) {
            element = expression.charAt(i);
            switch (element) {
                case '|':
                    sec = stack.pop();
                    fir = stack.pop();
                    elem = actUnit(fir, sec);
                    stack.push(elem);
                    break;
                case '*':
                    fir = stack.pop();
                    elem = actStar(fir);
                    stack.push(elem);
                    break;
                case '+':
                    sec = stack.pop();
                    fir = stack.pop();
                    elem = actJoin(fir, sec);
                    stack.push(elem);
                    break;
                default:
                    elem = actElem(element);
                    stack.push(elem);
            }
        }
        System.out.println("正则表达式已转换为 NFA!");
        elem = stack.pop();
        return elem;
    }

    // 打印 NFA
    public static void display(Elem elem) {
        System.out.println("NFA States:");
        System.out.println("Start State: " + elem.startName.nodeName);
        System.out.println("End State: " + elem.endName.nodeName);

        System.out.println("NFA Transitions:");
        for (int i = 0; i < elem.edgeCount; i++) {
            System.out.println("Edge " + (i + 1) + ": " + elem.edgeSet[i].startName.nodeName
                    + " --(" + elem.edgeSet[i].tranSymbol + ")--> " + elem.edgeSet[i].endName.nodeName);
        }

        System.out.println("End");
    }

    // 生成 NFA 的 DOT 文件
    public static void generateDotFileNFA(Elem nfa) {
        String fileName = "D:\\bianyi\\Regex2\\result\\nfa_graph.dot";
        try (BufferedWriter dotFile = new BufferedWriter(new FileWriter(fileName))) {
            dotFile.write("digraph NFA {\n");
            dotFile.write("  rankdir=LR;  // 横向布局\n\n");
            dotFile.write(" node [shape = circle];   // 状态节点\n\n");

            dotFile.write(nfa.endName.nodeName + " [shape=doublecircle];\n");

            // 添加 NFA 状态
            dotFile.write("  " + nfa.startName.nodeName + " [label=\"Start State: " + nfa.startName.nodeName + "\"];\n");
            dotFile.write("  " + nfa.endName.nodeName + " [label=\"End State: " + nfa.endName.nodeName + "\"];\n");

            // 添加 NFA 转移
            for (int i = 0; i < nfa.edgeCount; i++) {
                dotFile.write("  " + nfa.edgeSet[i].startName.nodeName + " -> " + nfa.edgeSet[i].endName.nodeName + " [label=\"" + nfa.edgeSet[i].tranSymbol + "\"];\n");
            }

            dotFile.write("}\n");

            System.out.println("NFA DOT 文件生成成功。");
        } catch (IOException e) {
            System.err.println("无法打开 DOT 文件。");
        }
        // 生成文件后立即调用 readDotFile
        readDotFile(fileName);
    }

    // 读取 DOT 文件并生成图像
    public static void readDotFile(String fileName) {
        try (BufferedReader dotFile = new BufferedReader(new FileReader(fileName))) {
            System.out.println("\n读取 DOT 文件：");
            String line;
            while ((line = dotFile.readLine()) != null) {
                System.out.println(line);  // 直接输出文件内容
            }
        } catch (IOException e) {
            System.err.println("无法读取 DOT 文件：" + fileName);
        }

        // 执行 Graphviz 命令，将 DOT 文件转为 PNG 图像
        String outputFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".png";
        String command = "dot -Tpng \"" + fileName + "\" -o \"" + outputFileName + "\"";

        System.out.println("执行命令： " + command);

        try {
            Process process = Runtime.getRuntime().exec(command);
            process.waitFor();
            System.out.println("图像生成成功：" + outputFileName);
        } catch (IOException | InterruptedException e) {
            System.err.println("生成图像失败。");
        }
    }
}
